﻿// Sami2Srt.cpp: implementation of the CSami2Srt class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Sami2Srt.h"
#include "UnicodeText.h"
#include "SmallUtilz.h"
#include "ConvISO8859_1.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define COUNTOF(a)	(sizeof(a)/sizeof(a[0]))
#define MAXSUBCOUNT	100

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSami2Srt::CSami2Srt() {}

CSami2Srt::~CSami2Srt() {}

//////////////////////////////////////////////////////////////////////
// 태그 찾기 관련 메쏘드
//////////////////////////////////////////////////////////////////////

bool CSami2Srt::bNextTag(TCHAR *tc, long lLen, long lStart, long &lFrom, long &lTo)
{
	while (lStart<lLen) {
		if (tc[lStart] == _TCHAR('<')) break;
		lStart++;
	}

	if (lStart == lLen) return false;
	lFrom = lStart;

	while (lStart<lLen) {
		if (tc[lStart] == _TCHAR('>')) break;
		lStart++;
	}
	if (lStart == lLen) return false;

	lTo = lStart;

	return true;
}

bool CSami2Srt::bNextSYNC(TCHAR *tc, long lLen, long lStart, long &lFrom, long &lTo)
{
	bool b = bNextTag(tc, lLen, lStart, lFrom, lTo);
	while (b) {
		if (_tcsnicmp(tc+lFrom+1, _T("sync"), 4) == 0) return true;
		lStart = lTo+1;
		b = bNextTag(tc, lLen, lStart, lFrom, lTo);
	}

	return false;
}

bool CSami2Srt::bNextP(TCHAR *tc, long lLen, long lStart, long &lFrom, long &lTo)
{
	bool b = bNextTag(tc, lLen, lStart, lFrom, lTo);
	while (b) {
		if (tc[lFrom+1] == _TCHAR('P') || tc[lFrom+1] == _TCHAR('p')) return true;
		lStart = lTo+1;
		b = bNextTag(tc, lLen, lStart, lFrom, lTo);
	}

	return false;
}

long CSami2Srt::lFinalTag(TCHAR *tc, long lLen, long lStart)
{
	long lFrom, lTo;
	bool b = bNextTag(tc, lLen, lStart, lFrom, lTo);
	while (b) {
		if (_tcsnicmp(tc+lFrom+1, _T("/body"), 5) == 0) return lFrom;
		if (_tcsnicmp(tc+lFrom+1, _T("/sami"), 5) == 0) return lFrom;
		lStart = lTo+1;
		b = bNextTag(tc, lLen, lStart, lFrom, lTo);
	}

	return lLen;
}

//////////////////////////////////////////////////////////////////////
// 태그에서 필요값 추출
//////////////////////////////////////////////////////////////////////

DWORD CSami2Srt::dwExtractTime(TCHAR *tc, long lFrom, long lTo)
{
	DWORD dwRet = 0;
	for (long l=lFrom; l<lTo; l++) {
		TCHAR tc0 = tc[l];
		if (_istdigit(tc0))
			dwRet = dwRet*10+(tc0-_TCHAR('0'));
	}

	return dwRet;
}

CString CSami2Srt::csExtractCC(TCHAR *tc, long lFrom, long lTo)
{
	CString csRet;

	for (long l=lFrom+2; l<lTo; l++) {
		if (_istalnum(tc[l]))
			csRet += static_cast<_TCHAR>(_totupper(tc[l]));
		else
			csRet += _TCHAR(' ');
	}

	csRet.TrimLeft();
	if (csRet.Left(5).Compare(_T("CLASS")) == 0) csRet.Delete(0, 5);
	csRet.TrimLeft();

	int pos = csRet.Find(_TCHAR(' '));
	if (pos>=0) {
		csRet = csRet.Left(pos);
	}

	return csRet;
}

//////////////////////////////////////////////////////////////////////
// CStringArray 관련 메쏘드
//////////////////////////////////////////////////////////////////////

int CSami2Srt::iGetCCNo(CStringArray &csa, CString &cs)
{
	// 이 메쏘드에선 애매하면 0을 리턴함
	// 즉, 그리 정직하기만 한 메쏘드는 아님

	if (csa.GetSize() == 0) return 0;

	int ret = findInCStringArray(csa, cs);
	if (ret<0) ret=0;

	return ret;
}

//////////////////////////////////////////////////////////////////////
// 태그 찾기 관련 메쏘드
//////////////////////////////////////////////////////////////////////

bool CSami2Srt::bGetNextTag(TCHAR *tBuf, long lLen, long lBegin, long &lFrom, long &lTo, CString &csTag)
{
	long l;
	bool bFound = false;

	for (l=lBegin; l<lLen; l++) {
		if (tBuf[l] == _TCHAR('<')) {
			bFound = true;
			lFrom = l;
			break;
		}
	}

	if (!bFound) return false;

	bFound = false;

	for (++l; l<lLen; l++) {
		if (tBuf[l] == _TCHAR('>')) {
			bFound = true;
			lTo = l;
			break;
		}
	}

	csTag = _T("");
	if (!bFound) lTo = lLen-1;

	for (l=lFrom+1; l<=lTo; l++)
		csTag += tBuf[l];

	if (csTag.Right(1).Compare(_T(">")) == 0)
		csTag = csTag.Left(csTag.GetLength()-1);

	return true;
}

//////////////////////////////////////////////////////////////////////
// 잘못되거나 줄일 수 있는 자막 줄이기
//////////////////////////////////////////////////////////////////////

CString CSami2Srt::csFixSubs(TCHAR *tSami, long lFrom, long lTo)
{
	/*
	1. 맨 마지막 CR 추가
	2. &*;(ISO-8859-1) -> ' '
	3. <br~> -> CR
	4. <i~>, <I~> -> <i>
	   </i~>, </I~> -> </i>
	   <b~>, <B~> -> <b>
	   </b~>, </B~> -> </b>
	   <u~>, <U~> -> <u>
	   </u~>, </U~> -> </u>
	   <font~>, <FONT~> -> <font color='#AAAA'>
	   </font~>, </FONT~> -> </font>
	5. 모든 <i> : <i>~</i> 내에 ' ', CR만 있으면 <i>, </i> 제거
	              단, </i>가 없으면 맨 마지막까지 확인해서 <i> 제거
	   모든 <b> : <b>~</b> 내에 ' ', CR만 있으면 <b>, </b> 제거
	              단, </b>가 없으면 맨 마지막까지 확인해서 <b> 제거
	   모든 <u> : <u>~</u> 내에 ' ', CR만 있으면 <u>, </u> 제거
	              단, </u>가 없으면 맨 마지막까지 확인해서 <u> 제거
	   모든 <font>: <font color>~</font> 내에 ' ', CR만 있으면 <font>, </font> 제거
	              단, </font>가 없으면 맨 마지막까지 확인해서 <font> 제거
	   5번 무한반복
	6. ' '+CR에서 ' '제거, 무한반복
	7. CR+CR -> CR, 무한반복
	8. 맨 마지막 CR 제거, 무한반복

	이 결과, 내용이 없으면 그 행은 없는 것임
	*/
	long lNewLen = lTo-lFrom+1;

	TCHAR *tBuf1 = new TCHAR[lNewLen+2];
	ZeroMemory(tBuf1, sizeof(TCHAR)*(lNewLen+2));
	long lSize1;

	// \r, \n 은 이미 \r로 완전 통일되었음(0x0d)

	long l1=0, l2;

	// 1. 버퍼로 복사하고, 맨 마지막 CR 추가
	lSize1 = lTo-lFrom+1;
	memcpy(tBuf1, tSami+lFrom, sizeof(TCHAR)*lSize1);
	tBuf1[lSize1++] = _TCHAR('\r');
	tBuf1[lSize1] = _TCHAR(0);
	//tBuf1에 lSize1개의 TCHAR 문자가 들어있음


	//두번째 버퍼 생성 필요
	TCHAR *tBuf2 = new TCHAR[lNewLen+2];
	ZeroMemory(tBuf2, sizeof(TCHAR)*(lNewLen+2));
	long lSize2;

	// 2. &*;(ISO-8859-1) -> ' '

	l2=0;
	for (l1=0; l1<lSize1; /*l1++, */l2++) {
		TCHAR tc;
		int len;
		if (CConvISO8859_1::convOne(tBuf1+l1, false, tc, len)) {
			tBuf2[l2] = tc;
			l1+=len;
		} else {
			tBuf2[l2] = tBuf1[l1];
			l1++;
		}
	}

	lSize2 = l1;
	tBuf2[lSize2] = _TCHAR(0);
	//tBuf2에 lSize2개의 TCHAR가 들어있음

	// 3. <br~> -> CR
	// 4. <i~>, <I~> -> <i>
	//    </i~>, </I~> -> </i>
	//    <b~>, <B~> -> <b>
	//    </b~>, </B~> -> </b>
	//    <u~>, <U~> -> <u>
	//    </u~>, </U~> -> </u>
	//    <font~>, <FONT~> -> <font color='#AAAAAA'>
	//    </font~>, </FONT~> -> </font>
	// 3.4를 동시에 시행
	CString csTag;
	l1 = 0;
	for (l2=0; l2<lSize2;) {
		bool bTag = bGetNextTag(tBuf2, lSize2, l2, lFrom, lTo, csTag);
		long lRest;
		if (!bTag) {
			lRest = lSize2-l2;
			memcpy(tBuf1+l1, tBuf2+l2, sizeof(TCHAR)*lRest);
			l1+=lRest;
			break;
		}

		lRest = lFrom-l2;
		memcpy(tBuf1+l1, tBuf2+l2, sizeof(TCHAR)*lRest);
		l1+=lRest;
		l2+=lRest;

		csTag.TrimLeft();
		csTag.TrimRight();

		TCHAR tcTag[6] = {_T('\0'), };
		_tcsncpy_s(tcTag, 6, static_cast<LPCTSTR>(csTag), 5);
		tcTag[0] = _totlower(tcTag[0]);
		tcTag[1] = _totlower(tcTag[1]);
		TCHAR tcNewTag[5] = {_T('\0'), };
		tcNewTag[0] = _TCHAR('<');

		switch (tcTag[0]) {
		case _T('b'):
		case _T('i'):
		case _T('u'):
			if (
				(tcTag[0]==_TCHAR('b')) &&
				(tcTag[1]==_TCHAR('r'))
				) {
				//<br>
				tBuf1[l1++] = _TCHAR('\r');
			} else {
				//<b~>, <B~> -> <b>
				//<i~>, <I~> -> <i>
				//<u~>, <U~> -> <u>
				tcNewTag[1] = tcTag[0];
				tcNewTag[2] = _TCHAR('>');
				tcscpyWOzero(tBuf1, l1, tcNewTag);
			}
			break;

		case _T('f'):
			if (_tcsnicmp(tcTag, _T("font"), 4) == 0) {
				CString csTag2;
				csTag.Delete(0, 4);
				csTag.TrimLeft();
				csTag.TrimRight();
				csTag = _T("<font ")+csTag+_T(">");
				tcscpyWOzero(tBuf1, l1, static_cast<LPCTSTR>(csTag));
			}
			break;

		case _T('/'):
			{
				switch (tcTag[1]) {
				case _T('b'):
				case _T('i'):
				case _T('u'):
					tcNewTag[1] = _TCHAR('/');
					tcNewTag[2] = tcTag[1];
					tcNewTag[3] = _TCHAR('>');
					tcscpyWOzero(tBuf1, l1, tcNewTag);
					break;

				default:
					if (csTag.Left(5).CompareNoCase(_T("/font")) == 0) {
						//</font~>, </FONT~> -> </font>
						tcscpyWOzero(tBuf1, l1, _T("</font>"));
					}
				}
			}
		}

		l2 = lTo+1;
	}

	//5. 모든 <i> : <i>~</i> 내에 ' ', CR만 있으면 <i>, </i> 제거
	//          단, </i>가 없으면 맨 마지막까지 확인해서 <i> 제거
	//   모든 <b> : <b>~</b> 내에 ' ', CR만 있으면 <b>, </b> 제거
	//          단, </b>가 없으면 맨 마지막까지 확인해서 <b> 제거
	//   모든 <u> : <u>~</u> 내에 ' ', CR만 있으면 <u>, </u> 제거
	//          단, </u>가 없으면 맨 마지막까지 확인해서 <u> 제거
	//   모든 <font>: <font color>~</font> 내에 ' ', CR만 있으면 <font>, </font> 제거
	//          단, </font>가 없으면 맨 마지막까지 확인해서 <font> 제거
	//   5번 무한반복

	tBuf1[l1] = _TCHAR(0);
	CString cs(tBuf1);
	cs.TrimLeft();
	bool bFound;

	// 태그가 없는 경우는 아예 들어갈 필요가 없음
	if (cs.Find(_TCHAR('<')) >= 0) {
		do {
			bFound = false;
			bool bEndTag, bText;
			TCHAR tch;

			static LPCTSTR lpTagsOpen[] = {
				_T("<i"),	_T("<b"),	_T("<u"),	_T("<font")
			};
			static LPCTSTR lpTagsClose[] = {
				_T("</i>"),	_T("</b>"),	_T("</u>"),	_T("</font>")
			};

			// 무의미한 태그들이 발견되면 태그들만 삭제

			for (int i=0; i<COUNTOF(lpTagsOpen); i++) {
				for (int j=0; j<cs.GetLength(); j++) {
					j = cs.Find(lpTagsOpen[i], j);
					if (j<0) break;
					int posClose = cs.Find(_TCHAR('>'), j+2);	// 굳이 _tcslen()을 쓸 필요까진 없음
					if (posClose<0) break;
					int l = cs.Find(lpTagsClose[i], posClose+1);
					if (l<0) {
						l = cs.GetLength();
					}
					bEndTag = (l>=0);
					
					bText = false;
					for (int k=posClose+1; k<l; k++) {
						tch = cs.GetAt(k);
						if (tch > _TCHAR(' ')) {
							bText = true;
							break;
						}
					}
					
					if (!bText) {
						if (bEndTag) {
							cs.Delete(l, _tcslen(lpTagsClose[i]));
						}
						cs.Delete(j, posClose-j+1);
						bFound = true;
					} else {
						j=posClose;
					}
				}
			}
		} while (bFound);
	}

	//6. ' '+CR에서 ' '제거, 무한반복
	int iCR;
	do {
		iCR = cs.Find(_T(" \r"));
		if (iCR>=0) {
			cs.Delete(iCR, 1);
		}
	} while (iCR>=0);

	//7. CR+CR -> CR, 무한반복
	do {
		iCR = cs.Find(_T("\r\r"));
		if (iCR>=0) {
			cs.Delete(iCR, 1);
		}
	} while (iCR>=0);

	//8. 맨 마지막 CR 제거, 무한반복

	cs.TrimRight();

	delete []tBuf2;
	delete []tBuf1;

	return cs;
}

void CSami2Srt::InsertSubWTime(CStringArray &csa, CString &csSubTime)
{
	//CSA에 자막 삽입
	//자막은 time code와 함께 삽입
	//자막포맷: 000000000:자막
	//8자리면 충분 (27시간)하지만, 그냥 9자리 사용.
	if (csSubTime.GetLength()<10) return;
	
	int size = csa.GetSize();
	if (!size) {
		csa.Add(csSubTime);
		return;
	}

	for (int i=0; i<size; i++) {
		CString cs = csa.GetAt(i);
		if (_tcsncmp(LPCTSTR(cs), LPCTSTR(csSubTime), 10) == 0) {
			// 같은 시간이 두 번 등장하는 개떡같은 경우
			cs.TrimRight();
			CString cs2(csSubTime);
			cs2.Delete(0, 10);
			cs2.TrimLeft();
			if ((cs.GetLength()>10) && cs2.GetLength()) {
				cs += (_TCHAR('\r')+cs2);
			} else {
				cs+=cs2;
			}
			csa.SetAt(i, cs);
			return;
		} else if (cs.Compare(csSubTime)>0) {
			csa.InsertAt(i, csSubTime);
			return;
		}
	}
	
	csa.Add(csSubTime);
}

TCHAR * CSami2Srt::tcSubRipText(CStringArray &csaSub)
{
	//1
	//00:00:20,000 --> 00:00:24,400
	//자막내용

	//자막 일련번호는 999999 까지 (6자리)
	//일련번호: 6+1 Bytes
	//시간: 29+1 Bytes
	//자막내용+2 Bytes (엔터 2번)
	//예비 4바이트
	//
	//기본적으로 자막 하나당 7+30+2+4=43 Bytes 소요

	//저장되어있는 자막은 000000000:자막 포맷
	//시간 코드는 9자리

	int csasize = csaSub.GetSize();
	long lSize = (6+1+29+1+2+4)*csasize;

	for (int i=0; i<csasize; i++) {
		lSize += csaSub.GetAt(i).GetLength();
	}
	lSize++;	// 마지막 0 자리

	TCHAR *tcSrt = new TCHAR[lSize];

	int pos=0;
	long lSrtCount=0;

	for (int i=0; i<csasize; i++) {
		CString csSub = csaSub.GetAt(i);

		if (csSub.GetLength()<=10) {
			continue;
		}

		_stprintf_s(tcSrt+pos, lSize-pos, _T("%d"), ++lSrtCount);
		while (tcSrt[pos]) pos++;
		tcSrt[pos++] = _TCHAR('\r');

		long lTime1 = _ttol(static_cast<LPCTSTR>(csSub));
		long lTime2;

		if (i>=(csasize-1)) {
			// 다음 자막 즉, 닫는 태그 등이 없다.
			// 맘 편하게 현 자막 + 10초
			lTime2 = lTime1+10000;
		} else {
			// 다음 자막이 공백이면 그 놈을 닫는 시간으로 적용
			// 그렇지 않으면 다음 자막 -40ms
			// 그런데, 다음 자막과 시간 차가 40ms 이내라면 그냥 다음 자막 -1ms

			// 위의 룰 다 필요 없음
			// 다음 자막 시간을 그냥 닫는 시간으로 적용

			CString csSub2 = csaSub.GetAt(i+1);
			lTime2 = _ttol(static_cast<LPCTSTR>(csSub2));
		}

		//00:00:20,000 --> 00:00:24,400
		int h1, m1, s1, ms1;
		int h2, m2, s2, ms2;

		CSmallUtilz4iTrans::ms2HMSMS(lTime1, h1, m1, s1, ms1);
		CSmallUtilz4iTrans::ms2HMSMS(lTime2, h2, m2, s2, ms2);

		_stprintf_s(tcSrt+pos, lSize-pos, _T("%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d"), h1, m1, s1, ms1, h2, m2, s2, ms2);
		while (tcSrt[pos]) pos++;
		tcSrt[pos++] = _TCHAR('\r');

		csSub.Delete(0, 10);
		
		// 마지막 정리
		// 한 줄에 태그나 공백만 있고 내용이 없는 경우 엔터 제거
		{
			int len = csSub.GetLength();
			int p = 0;
			int linebegin = 0;
			bool bContentFound = false;
			CString csSubFixed;
			while (p<=len) {
				//TCHAR now = csSub.GetAt(pos);
				TCHAR now = LPCTSTR(csSub)[p];

				if (now == _TCHAR('\r') || now == _TCHAR(0)) {
					// 행의 끝 또는 자막의 끝
					// 컨텐츠가 없거나 자막의 끝이면 '\r' 빼고 복사
					csSubFixed += csSub.Mid(linebegin, p-linebegin);
					if (bContentFound && (now != _TCHAR(0))) {
						csSubFixed += _TCHAR('\r');
					}
					if (now == _TCHAR(0)) {
						break;
					} else {
						p++;
						linebegin = p;
					}
				} else if (csSub.GetAt(p) == _TCHAR(' ')) {
					p++;
				} else if (csSub.GetAt(p) == _TCHAR('<')) {
					int closePos = csSub.Find(_TCHAR('>'), p);
					if (closePos<0) {
						// 태그가 안 끝나는 등신같은 경우
						// 그냥 맨 끝까지 복사하고 땡침
						csSubFixed += csSub.Mid(linebegin);
						break;
					} else {
						p=closePos+1;
					}
				} else {
					p++;
					bContentFound = true;
				}
			}
			csSub = csSubFixed;
		}

		_tcscpy_s(tcSrt+pos, lSize-pos, static_cast<LPCTSTR>(csSub));
		while (tcSrt[pos]) pos++;
		tcSrt[pos++] = _TCHAR('\r');
		tcSrt[pos++] = _TCHAR('\r');
	}
	tcSrt[pos] = 0;

	return tcSrt;
}


//////////////////////////////////////////////////////////////////////
// 외부 노출 메쏘드
//////////////////////////////////////////////////////////////////////

int CSami2Srt::iConvert(LPCTSTR lpSami, CStringArray &csaSrts, LPCTSTR lpWriteFolder)
{
	CUnicodeText unicode;
	if (!unicode.bReadTextfile(lpSami))
		return -1;

	// 이 시점에서 tSami[]에는 lSamiSize 개의 유니코드 데이터가 저장되어 있음

	TCHAR *tSami = unicode.tcText;

	// 원래는 마지막을 가리키는 </body>나 </sami>를 바로 찾아야 정상인데,
	// </body>가 맨 앞에 나오는 제정신이 아닌 자막을 발견했음
	// 결론은 일단 자막을 하나 찾은 뒤에 그 다음에 나오는 </body>나 </sami>를 찾기로 했음
	long lSamiLen;
	{
		long lFrom, lTo;
		BOOL b = bNextSYNC(tSami, unicode.lLen, 0, lFrom, lTo);
		if (b) lSamiLen = lFinalTag(tSami, unicode.lLen, lTo);
		else lSamiLen = lFinalTag(tSami, unicode.lLen);
	}
	tSami[lSamiLen] = _TCHAR(0);

	// 마지막 태그 이후 삭제. 즉, 이젠 무조건 EOF만 보고 달리는 거임

	//////////////////////////////////////////////////////////////////////
	// 일단 몇개국어가 들어있는지 확인하고 모으기
	//////////////////////////////////////////////////////////////////////

	CStringArray csaCCs;
	long lFrom, lTo;
	BOOL b = bNextP(tSami, lSamiLen, 0, lFrom, lTo);
	CString csCC;

	while (b) {
		csCC = csExtractCC(tSami, lFrom, lTo);
		if (findInCStringArray(csaCCs, csCC)<0) {
			csaCCs.Add(csCC);
		}

		b = bNextP(tSami, lSamiLen, lTo+1, lFrom, lTo);
	}

	// 이제 CC의 목록만 만들었음 (KRCC/ENCC 등)
	int iCCCount = csaCCs.GetSize();
	if (iCCCount<=0) {
		// CC가 하나도 없다. 이런 썅!
		return 0;
	} else if (iCCCount >= MAXSUBCOUNT) {
		iCCCount = MAXSUBCOUNT;
	}

	CStringArray csaSubs[MAXSUBCOUNT];

	// CC별 CSA 만들고 죽죽 뽑고... 해야 됨
	// Sync, P 읽은 뒤 처리 반복...

	// 첫 <Sync> 태그 찾기
	b = bNextSYNC(tSami, lSamiLen, 0, lFrom, lTo);
	DWORD dwTime=0;
	csCC = _T("");
	if (b) {
		dwTime = dwExtractTime(tSami, lFrom, lTo);
		b = bNextP(tSami, lSamiLen, lTo+1, lFrom, lTo);
		if (b) csCC = csExtractCC(tSami, lFrom, lTo);
	}

	long lStrBegin;
	DWORD dwTime2Write = dwTime;
	CString csCCtoWrite(csCC);
	CString csSub;
	int iCCpos;

	b = bNextSYNC(tSami, lSamiLen, 0, lFrom, lTo);

	long lFromP, lToP;
	bool bP, bMoreP;

	while (b) {
		dwTime = dwExtractTime(tSami, lFrom, lTo);

		b = bNextP(tSami, lSamiLen, lTo+1, lFrom, lTo);
		if (b) {
			csCC = csExtractCC(tSami, lFrom, lTo);
		} else {
			// 더 이상의 <p>가 없으면 더 읽을 필요 없음
			break;
		}

		// 현시점 상황
		// lTo는 <p ~~~>의 끝을 가리키고 있음
		// 더 이상의 <sync>가 있는지 없는지는 알 수 없음

reentry:

		//lTo+1부터 문자열이 들어있음
		lStrBegin = lTo+1;

		b = bNextSYNC(tSami, lSamiLen, lStrBegin, lFrom, lTo);
		bP = bNextP(tSami, lSamiLen, lStrBegin, lFromP, lToP);
		// b가 true라면 그 다음 자막이 존재하는 것
		// false라면 이 자막이 마지막 자막
		// bP가 true이고, lFromP가 lFrom보다 작으면 또 하나의 <P>가 존재함

		// (정리)
		// b가 true일 때 bP가 true이고, lFromP가 lFrom보다 작으면 <P>가 또 존재
		// bP가 false이거나 lFromP가 lFrom보다 크면 기존과 동일
		// b가 false일 때 bP가 true이면 <P>가 또 존재

		// 추가 P가 존재하는 경우 csCC를 확인해서 reentry로 돌아감

		bMoreP = (bP) && ((!b) || (lFromP<lFrom));

		if (bMoreP) {
			b = bP;
			lFrom = lFromP;
			lTo = lToP;
		}

		csSub = csFixSubs(tSami, lStrBegin, (b)?(lFrom-1):(lSamiLen-1));

		iCCpos = iGetCCNo(csaCCs, csCC);
		if ((iCCpos>=0) && (iCCpos<MAXSUBCOUNT)) {
			CString csTimeCode;
			csTimeCode.Format(_T("%09u:"), dwTime);
			csTimeCode += csSub;

			InsertSubWTime(csaSubs[iCCpos], csTimeCode);
		}

		if (bMoreP) {
			csCC = csExtractCC(tSami, lFrom, lTo);
			goto reentry;
		}


		//이미 다음 자막 존재여부를 위에서 확인했기 때문에 b에 대한 다른 처리는 필요 없음
	}

	//입력은 SMI의 파일명임
	// c:\hello.smi
	//출력은 SRT의 파일명들
	// c:\hello.smi.1.srt
	// 이런 포맷이면 되겠다

	//그리고, CC의 목록도 함께 넘겨야 됨
	//그래야 국가 코드에 따른 처리 가능

	//넘길 때는 "KRCC"c:\hello... 이런 포맷

	csaSrts.RemoveAll();

	static int nSubCount=100;

	for (int i=0; i<iCCCount; i++) {
		CString csFilename;
		csFilename.Format(_T("%s.%03d.srt"), lpSami, ++nSubCount);
		csFilename.Delete(0, csFilename.ReverseFind(_TCHAR('\\'))+1);
		csFilename = lpWriteFolder+csFilename;

		TCHAR *tcOut = tcSubRipText(csaSubs[i]);
		CUnicodeText unicodesrt;
		unicodesrt.tcText = tcOut;
		unicodesrt.lLen = _tcslen(tcOut);
		unicodesrt.WriteTextfile(csFilename);

		csFilename = _T("\"")+csaCCs.GetAt(i)+_T("\"")+csFilename;
		csaSrts.Add(csFilename);
	}

	return iCCCount;
}


void CSami2Srt::tcscpyWOzero(TCHAR *tcDst, long &lPos, LPCTSTR str)
{
	int len = _tcslen(str);
	memcpy(tcDst+lPos, str, sizeof(TCHAR)*len);
	lPos+=len;
}

int CSami2Srt::findInCStringArray(CStringArray &csa, LPCTSTR lp)
{
	for (int i=0; i<csa.GetSize(); i++) {
		if (csa.GetAt(i).Compare(lp) == 0) return i;
	}
	return -1;
}
